/*-------------------------------------------------------------------------
 *
 * parser.c
 *		PostgreSQL 语法的主入口/驱动程序
 *
 * 请注意，语法不允许执行任何表访问
 * （因为我们需要能够在事务中中止时仍然进行基本解析）。因此，语法返回的数据
 * 结构是“原始”的解析树，这些解析树仍需要由 analyze.c 和相关文件进行分析。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/parser/parser.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "mb/pg_wchar.h"
#include "parser/gramparse.h"
#include "parser/parser.h"
#include "parser/scansup.h"

static bool fc_check_uescapechar(unsigned char fc_escape);
static char *fc_str_udeescape(const char *fc_str, char fc_escape,
						   int fc_position, core_yyscan_t fc_yyscanner);


/*
 * raw_parser
 *		给定字符串形式的查询，执行词法和语法分析。
 *
 * 返回原始（未经分析的）解析树列表。列表的内容
 * 具有指定 RawParseMode 所需的格式。
 */
List * raw_parser(const char *fc_str, RawParseMode fc_mode)
{
	core_yyscan_t fc_yyscanner;
	base_yy_extra_type fc_yyextra;
	int			fc_yyresult;

	/* 初始化 flex 扫描仪 */
	fc_yyscanner = scanner_init(fc_str, &fc_yyextra.core_yy_extra,
							 &ScanKeywords, ScanKeywordTokens);

	/* base_yylex() 只需要我们初始化前瞻令牌（如果有的话） */
	if (fc_mode == RAW_PARSE_DEFAULT)
		fc_yyextra.have_lookahead = false;
	else
	{
		/* 该数组由 RawParseMode 枚举索引 */
		static const int fc_mode_token[] = {
			0,					/* RAW_PARSE_DEFAULT */
			MODE_TYPE_NAME,		/* RAW_PARSE_TYPE_NAME */
			MODE_PLPGSQL_EXPR,	/* RAW_PARSE_PLPGSQL_EXPR */
			MODE_PLPGSQL_ASSIGN1,	/* RAW_PARSE_PLPGSQL_ASSIGN1 */
			MODE_PLPGSQL_ASSIGN2,	/* RAW_PARSE_PLPGSQL_ASSIGN2 */
			MODE_PLPGSQL_ASSIGN3	/* RAW_PARSE_PLPGSQL_ASSIGN3 */
		};

		fc_yyextra.have_lookahead = true;
		fc_yyextra.lookahead_token = fc_mode_token[fc_mode];
		fc_yyextra.lookahead_yylloc = 0;
		fc_yyextra.lookahead_end = NULL;
	}

	/* 初始化 bison 解析器 */
	parser_init(&fc_yyextra);

	/* Parse! */
	fc_yyresult = base_yyparse(fc_yyscanner);

	/* 清理（释放内存） */
	scanner_finish(fc_yyscanner);

	if (fc_yyresult)				/* 错误 */
		return NIL;

	return fc_yyextra.parsetree;
}


/*
 * 解析器与核心词法分析器（scan.l 中的 core_yylex）之间的
 * 中间过滤器。
 *
 * 需要此过滤器，因为在某些情况下，标准 SQL 文法
 * 需要多个令牌的前瞻。我们通过在这里替换令牌将这些情况
 * 减少到一个令牌的前瞻，以保持文法为 LALR(1)。
 *
 * 使用过滤器比直接在 scan.l 中识别多字令牌更简单，因为
 * 我们必须允许词之间的注释。此外，不清楚该如何做到
 * 而不重新引入扫描器回溯，这会带来比这个过滤层
 * 更多的性能损失。
 *
 * 我们还使用此过滤器将 UIDENT 和 USCONST 序列转换为
 * 普通的 IDENT 和 SCONST 令牌。虽然这可以通过主文法中的
 * 额外产生式处理，但这样做更有效率。
 *
 * 此过滤器还提供了一个便捷的位置，用于在线性表示
 * core_YYSTYPE 和 YYSTYPE 之间进行转换（实际上它们
 * 是同一事物，但在符号上是不同的）。
 */
int base_yylex(YYSTYPE *fc_lvalp, YYLTYPE *fc_llocp, core_yyscan_t fc_yyscanner)
{
	base_yy_extra_type *fc_yyextra = pg_yyget_extra(fc_yyscanner);
	int			fc_cur_token;
	int			fc_next_token;
	int			fc_cur_token_length;
	YYLTYPE		fc_cur_yylloc;

#ifdef FDD
	fc_yyextra->core_yy_extra.gb18030Flag = (GetDatabaseEncoding() == PG_GB18030 ? 1 : 0);
#endif //FDD
	/* 获取下一个token --- 我们可能已经有了它 */
	if (fc_yyextra->have_lookahead)
	{
		fc_cur_token = fc_yyextra->lookahead_token;
		fc_lvalp->core_yystype = fc_yyextra->lookahead_yylval;
		*fc_llocp = fc_yyextra->lookahead_yylloc;
		if (fc_yyextra->lookahead_end)
			*(fc_yyextra->lookahead_end) = fc_yyextra->lookahead_hold_char;
		fc_yyextra->have_lookahead = false;
	}
	else
		fc_cur_token = core_yylex(&(fc_lvalp->core_yystype), fc_llocp, fc_yyscanner);

	/*
	 * 如果此令牌不是需要前瞻的令牌，直接返回它。如果它
	 * 需要前瞻，则确定令牌长度。 （我们可以通过 strlen()
	 * 获取，但由于可能性集合很小，硬编码似乎
	 * 可行且效率更高——至少对于固定长度的情况。）
	 */
	switch (fc_cur_token)
	{
		case NOT:
			fc_cur_token_length = 3;
			break;
		case NULLS_P:
			fc_cur_token_length = 5;
			break;
		case WITH:
			fc_cur_token_length = 4;
			break;
		case UIDENT:
		case USCONST:
			fc_cur_token_length = strlen(fc_yyextra->core_yy_extra.scanbuf + *fc_llocp);
			break;
		default:
			return fc_cur_token;
	}

	/*
	 * 识别当前标记的结束+1。core_yylex()在这里暂时存储了一个
	 * '\0'，并且在我们再次调用它时会撤销这个操作。为了错误报告的目的，
	 * 我们需要重新执行它，以完全撤回前瞻调用。
	 */
	fc_yyextra->lookahead_end = fc_yyextra->core_yy_extra.scanbuf +
		*fc_llocp + fc_cur_token_length;
	Assert(*(fc_yyextra->lookahead_end) == '\0');

	/*
	 * 在调用周围保存和恢复 *llocp。看起来我们可以通过将 &lookahead_yylloc
	 * 传递给 core_yylex() 来避免这一点，但这不起作用，因为 flex 实际上在内部
	 * 保留了最后传递的指针，并将其用于错误报告。我们需要任何错误报告指向
	 * 当前标记，而不是下一个标记。
	 */
	fc_cur_yylloc = *fc_llocp;

	/* 获取下一个token，并将输出保存到lookahead变量中 */
	fc_next_token = core_yylex(&(fc_yyextra->lookahead_yylval), fc_llocp, fc_yyscanner);
	fc_yyextra->lookahead_token = fc_next_token;
	fc_yyextra->lookahead_yylloc = *fc_llocp;

	*fc_llocp = fc_cur_yylloc;

	/* 现在撤销当前标记的非截断 */
	fc_yyextra->lookahead_hold_char = *(fc_yyextra->lookahead_end);
	*(fc_yyextra->lookahead_end) = '\0';

	fc_yyextra->have_lookahead = true;

	/* 根据lookahead替换cur_token（如果需要） */
	switch (fc_cur_token)
	{
		case NOT:
			/* 如果NOT后面跟着BETWEEN、IN等，则将NOT替换为NOT_LA */
			switch (fc_next_token)
			{
				case BETWEEN:
				case IN_P:
				case LIKE:
				case ILIKE:
				case SIMILAR:
					fc_cur_token = NOT_LA;
					break;
			}
			break;

		case NULLS_P:
			/* 如果NULLS_P后面跟着FIRST或LAST，则将NULLS_P替换为NULLS_LA */
			switch (fc_next_token)
			{
				case FIRST_P:
				case LAST_P:
					fc_cur_token = NULLS_LA;
					break;
			}
			break;

		case WITH:
			/* 如果WITH后面跟着TIME或ORDINALITY，则将WITH替换为WITH_LA */
			switch (fc_next_token)
			{
				case TIME:
				case ORDINALITY:
					fc_cur_token = WITH_LA;
					break;
			}
			break;

		case UIDENT:
		case USCONST:
			/* 查看UESCAPE */
			if (fc_next_token == UESCAPE)
			{
				/* 没错，所以获取第三个token，最好是SCONST */
				const char *fc_escstr;

				/* 再次保存和恢复 *llocp */
				fc_cur_yylloc = *fc_llocp;

				/* 使当前标记非截断，以便错误指向第三个标记 */
				*(fc_yyextra->lookahead_end) = fc_yyextra->lookahead_hold_char;

				/* 获取第三个token */
				fc_next_token = core_yylex(&(fc_yyextra->lookahead_yylval),
										fc_llocp, fc_yyscanner);

				/* 如果我们在这里抛出错误，它将指向第三个标记 */
				if (fc_next_token != SCONST)
					scanner_yyerror("UESCAPE must be followed by a simple string literal",
									fc_yyscanner);

				fc_escstr = fc_yyextra->lookahead_yylval.str;
				if (strlen(fc_escstr) != 1 || !fc_check_uescapechar(fc_escstr[0]))
					scanner_yyerror("invalid Unicode escape character",
									fc_yyscanner);

				/* 现在恢复 *llocp；错误将指向第一个标记 */
				*fc_llocp = fc_cur_yylloc;

				/* 应用 Unicode 转换 */
				fc_lvalp->core_yystype.str =
					fc_str_udeescape(fc_lvalp->core_yystype.str,
								  fc_escstr[0],
								  *fc_llocp,
								  fc_yyscanner);

				/*
				 * 我们不需要撤销 UESCAPE 的非截断。我们希望做的是清除 have_lookahead，
				 * 从而消耗所有三个标记。
				 */
				fc_yyextra->have_lookahead = false;
			}
			else
			{
				/* 没有 UESCAPE，因此使用默认转义字符进行转换 */
				fc_lvalp->core_yystype.str =
					fc_str_udeescape(fc_lvalp->core_yystype.str,
								  '\\',
								  *fc_llocp,
								  fc_yyscanner);
			}

			if (fc_cur_token == UIDENT)
			{
				/* 这是一个标识符，因此按需截断 */
				truncate_identifier(fc_lvalp->core_yystype.str,
									strlen(fc_lvalp->core_yystype.str),
									true);
				fc_cur_token = IDENT;
			}
			else if (fc_cur_token == USCONST)
			{
				fc_cur_token = SCONST;
			}
			break;
	}

	return fc_cur_token;
}

/* 将十六进制数字（调用者应已验证）转换为值 */
static unsigned int fc_hexval(unsigned char fc_c)
{
	if (fc_c >= '0' && fc_c <= '9')
		return fc_c - '0';
	if (fc_c >= 'a' && fc_c <= 'f')
		return fc_c - 'a' + 0xA;
	if (fc_c >= 'A' && fc_c <= 'F')
		return fc_c - 'A' + 0xA;
	elog(ERROR, "invalid hexadecimal digit");
	return 0;					/* 未到达 */
}

/* Unicode 代码点可以接受吗？ */
static void fc_check_unicode_value(pg_wchar fc_c)
{
	if (!is_valid_unicode_codepoint(fc_c))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("invalid Unicode escape value")));
}

/* "escape"作为Unicode转义字符（UESCAPE语法）是否可接受？ */
static bool fc_check_uescapechar(unsigned char fc_escape)
{
	if (isxdigit(fc_escape)
		|| fc_escape == '+'
		|| fc_escape == '\''
		|| fc_escape == '"'
		|| scanner_isspace(fc_escape))
		return false;
	else
		return true;
}

/*
 * 处理 "str" 中的 Unicode 转义，生成一个 palloc 的普通字符串
 *
 * escape: 要使用的转义字符
 * position: U&'' 或 U&"" 字符串标记的起始位置
 * yyscanner: 错误报告所需的上下文信息
 */
static char * fc_str_udeescape(const char *fc_str, char fc_escape,
			  int fc_position, core_yyscan_t fc_yyscanner)
{
	const char *fc_in;
	char	   *fc_new,
			   *fc_out;
	size_t		fc_new_len;
	pg_wchar	fc_pair_first = 0;
	ScannerCallbackState fc_scbstate;

	/*
	 * 大致估计结果不会比输入长，但为 Unicode 转换留出足够的填充空间。
	 */
	fc_new_len = strlen(fc_str) + MAX_UNICODE_EQUIVALENT_STRING + 1;
	fc_new = palloc(fc_new_len);

	fc_in = fc_str;
	fc_out = fc_new;
	while (*fc_in)
	{
		/* 如有需要，扩大字符串 */
		size_t		fc_out_dist = fc_out - fc_new;

		if (fc_out_dist > fc_new_len - (MAX_UNICODE_EQUIVALENT_STRING + 1))
		{
			fc_new_len *= 2;
			fc_new = repalloc(fc_new, fc_new_len);
			fc_out = fc_new + fc_out_dist;
		}

		if (fc_in[0] == fc_escape)
		{
			/*
			 * 在处理此转义序列时报告的任何错误都将
			 * 具有指向转义的错误光标。
			 */
			setup_scanner_errposition_callback(&fc_scbstate, fc_yyscanner,
											   fc_in - fc_str + fc_position + 3);	/* 3个字符用于 U&" */
			if (fc_in[1] == fc_escape)
			{
				if (fc_pair_first)
					goto invalid_pair;
				*fc_out++ = fc_escape;
				fc_in += 2;
			}
			else if (isxdigit((unsigned char) fc_in[1]) &&
					 isxdigit((unsigned char) fc_in[2]) &&
					 isxdigit((unsigned char) fc_in[3]) &&
					 isxdigit((unsigned char) fc_in[4]))
			{
				pg_wchar	fc_unicode;

				fc_unicode = (fc_hexval(fc_in[1]) << 12) +
					(fc_hexval(fc_in[2]) << 8) +
					(fc_hexval(fc_in[3]) << 4) +
					fc_hexval(fc_in[4]);
				fc_check_unicode_value(fc_unicode);
				if (fc_pair_first)
				{
					if (is_utf16_surrogate_second(fc_unicode))
					{
						fc_unicode = surrogate_pair_to_codepoint(fc_pair_first, fc_unicode);
						fc_pair_first = 0;
					}
					else
						goto invalid_pair;
				}
				else if (is_utf16_surrogate_second(fc_unicode))
					goto invalid_pair;

				if (is_utf16_surrogate_first(fc_unicode))
					fc_pair_first = fc_unicode;
				else
				{
					pg_unicode_to_server(fc_unicode, (unsigned char *) fc_out);
					fc_out += strlen(fc_out);
				}
				fc_in += 5;
			}
			else if (fc_in[1] == '+' &&
					 isxdigit((unsigned char) fc_in[2]) &&
					 isxdigit((unsigned char) fc_in[3]) &&
					 isxdigit((unsigned char) fc_in[4]) &&
					 isxdigit((unsigned char) fc_in[5]) &&
					 isxdigit((unsigned char) fc_in[6]) &&
					 isxdigit((unsigned char) fc_in[7]))
			{
				pg_wchar	fc_unicode;

				fc_unicode = (fc_hexval(fc_in[2]) << 20) +
					(fc_hexval(fc_in[3]) << 16) +
					(fc_hexval(fc_in[4]) << 12) +
					(fc_hexval(fc_in[5]) << 8) +
					(fc_hexval(fc_in[6]) << 4) +
					fc_hexval(fc_in[7]);
				fc_check_unicode_value(fc_unicode);
				if (fc_pair_first)
				{
					if (is_utf16_surrogate_second(fc_unicode))
					{
						fc_unicode = surrogate_pair_to_codepoint(fc_pair_first, fc_unicode);
						fc_pair_first = 0;
					}
					else
						goto invalid_pair;
				}
				else if (is_utf16_surrogate_second(fc_unicode))
					goto invalid_pair;

				if (is_utf16_surrogate_first(fc_unicode))
					fc_pair_first = fc_unicode;
				else
				{
					pg_unicode_to_server(fc_unicode, (unsigned char *) fc_out);
					fc_out += strlen(fc_out);
				}
				fc_in += 8;
			}
			else
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("invalid Unicode escape"),
						 errhint("Unicode escapes must be \\XXXX or \\+XXXXXX.")));

			cancel_scanner_errposition_callback(&fc_scbstate);
		}
		else
		{
			if (fc_pair_first)
				goto invalid_pair;

			*fc_out++ = *fc_in++;
		}
	}

	/* 未完成的代理对？ */
	if (fc_pair_first)
		goto invalid_pair;

	*fc_out = '\0';
	return fc_new;

	/*
	 * 我们可能会在错误回调激活或未激活的情况下到达这里。
	 * 调用 scanner_errposition 以确保出现错误光标；
	 * 如果回调处于活动状态，这将是重复的，但没有害处。
	 */
invalid_pair:
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("invalid Unicode surrogate pair"),
			 scanner_errposition(fc_in - fc_str + fc_position + 3,	/* 3个字符用于 U&" */
								 fc_yyscanner)));
	return NULL;				/* 保持编译器安静 */
}
